//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include <drvpub.h>
#include <bulverde.h>
#include "display_def.h"

class CDisplay : public Driver {
public:
    CARAPI Read(
        /* [in] */ UINT64 u64Offset,
        /* [in] */ UINT uNumberOfBytesToRead,
        /* [out] */ EzByteBuf ebbData,
        /* [out] */ IEvent * * ppCompletionEvent);
    CARAPI Write(
        /* [in] */ UINT64 u64Offset,
        /* [in] */ EzByteBuf ebbData,
        /* [out] */ UINT * puNumberOfBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);
    CARAPI Control(
        /* [in] */ INT nControlCode,
        /* [in] */ EzByteBuf ebbInData,
        /* [out] */ EzByteBuf * pOutData,
        /* [out] */ IEvent * * ppCompletionEvent);
    virtual void Dispose();

public :
    ECode  CDisplay::GetOverlayOut(
        /* [in] */ int layer,
        /* [out] */ EzByteBuf * pOutData);

    DisplayContext m_disCxt;
    Address m_userFrameBufAddr[3];
    UINT    m_userFrameBufSize[3];
};
static CDisplay s_display;

ECode CDisplay::Read(
        /* [in] */ UINT64 u64Offset,
        /* [in] */ UINT uNumberOfBytesToRead,
        /* [out] */ EzByteBuf ebbData,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode CDisplay::Write(
        /* [in] */ UINT64 u64Offset,
        /* [in] */ EzByteBuf ebbData,
        /* [out] */ UINT * puNumberOfBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode  CDisplay::GetOverlayOut(
        /* [in] */ int layer,
        /* [out] */ EzByteBuf * pOutData)
{
    OverlayFormat *pOutOverlay;
    OverlayFormat *pDrvOverlay;
    Address *pShareBufAddr;
    UINT    *pShareBufSize;
    Address ShareAddr, PhysAddr;
    int nMapSize;
    ECode ec;

    if (pOutData->GetCapacity() < (int)sizeof(OverlayFormat)) {
        return E_OUT_OF_MEMORY;
    }

    pDrvOverlay = &m_disCxt.overlay[layer];
    if (layer == 1) {
        nMapSize =  ROUNDUP_PAGE(pDrvOverlay->Width * pDrvOverlay->Height)
                    >> PAGE_SHIFT;;
        PhysAddr = m_disCxt.paOverlay1;
    }
    else if (layer == 2) {
        nMapSize = OVERLAY2TOTAL_FB_MAXSIZE;
        PhysAddr = m_disCxt.paOverlay2Y;
    }
    else return E_INVALID_ARGUMENT;

    pShareBufAddr = &m_userFrameBufAddr[layer];
    pShareBufSize = &m_userFrameBufSize[layer];
    pOutOverlay = (OverlayFormat *)(char *)ebbOutData;

    if(pDrvOverlay->Enable == FALSE) return E_ACCESS_DENIED;
    memcpy(pOutOverlay, pDrvOverlay, sizeof(OverlayFormat));

    // Map the virtual address that user application can use
    ec = DzIoRemap( 0, nMapSize, PhysAddr,
                  0, Protect_RWVM, &ShareAddr);
    if (FAILED(ec))  return ec;

    pOutOverlay->pChannelFb[0] = (void*)ShareAddr;

    if (layer == 2) {
        pOutOverlay->pChannelFb[1] = (void *)(ShareAddr + OVERLAY2Y_FB_MAXSIZE);
        pOutOverlay->pChannelFb[2] = (void *)((Address)pOutOverlay->pChannelFb[1] +
                                  OVERLAY2Cb_FB_MAXSIZE);
    }

    *pShareBufAddr = ShareAddr;
    *pShareBufSize = nMapSize;
    pOutData->SetUsed(sizeof(OverlayFormat));

    return NOERROR;
}


ECode CDisplay::Control(
        /* [in] */ INT nControlCode,
        /* [in] */ EzByteBuf ebbInData,
        /* [out] */ EzByteBuf * pOutData,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    char *tBuf = NULL;
    ECode ec = NOERROR;

    int layer;

    switch(nControlCode) {
        case 0 :
            //cls
            memset((void *)m_disCxt.vaBasePlane, 0, BASEPLANE_FB_SIZE);
            break;

        case 1 ://get frame buffer physical address
        /* LCD   PhyscalBase | width | height | bitspp
        BITS64 ......   32  ... 20  ...   8  ...   0
        */
            assert(NULL != (char *)ebbOutData);
            tBuf = (char*)ebbOutData;
            *(UINT32*)tBuf = (( LCD_WIDTH & 0xfff ) << 20) |
                           ((LCD_HEIGHT & 0xfff) << 8) | (BASEPLANE_PIXELBITS & 0xff);

            ((UINT32*)tBuf)[1] = (UINT32)m_disCxt.paBasePlane;
            pOutData->SetUsed(sizeof(UINT32));
            break;

        case 3 : { //get frame buffer virtual address
            Address userLcdBaseAddr;

            assert(NULL != (char *)ebbOutData);
            tBuf = (char*)ebbOutData;

            // Init display hardware
            ec = Display_Init(&m_disCxt);
            if (FAILED(ec)) return ec;

            ec = DzIoRemap( 0, BASEPLANE_FB_SIZE, m_disCxt.paBasePlane,
                          0, Protect_RWVM, &userLcdBaseAddr);
            if (FAILED(ec)) return ec;

            ((UINT32*)tBuf)[0] = (UINT32)userLcdBaseAddr;
            pOutData->SetUsed(sizeof(UINT32));
            m_userFrameBufAddr[0] = userLcdBaseAddr;
            m_userFrameBufSize[0] = BASEPLANE_FB_SIZE;

            break;
        }

        case 7: {
            // Get baseplane format
            FrameBufferFormat fbFormat;

            assert(NULL != (char *)ebbOutData);

            fbFormat.Color = BASEPLANE_COLORFORMAT;
            fbFormat.Bpp = BASEPLANE_PIXELBITS;
            fbFormat.Width = LCD_WIDTH;
            fbFormat.Height = LCD_HEIGHT;
            pOutData->Copy(&fbFormat, sizeof(FrameBufferFormat));
            break;
        }

        case 8:
            // Set overlay, if success also return the current format
            OverlayFormat *pOverlayFormat;

            assert(NULL != (char *)ebbInData);

            pOverlayFormat = (OverlayFormat *)(char*)ebbInData;

            ec = Display_SetOverlay(&m_disCxt, pOverlayFormat);

            if (NOERROR == ec) {
                ec = GetOverlayOut(pOverlayFormat->Layer, ebbOutData);
            }
            break;

        case 9:
            if ((char *)ebbInData == NULL) layer = 2; // default: overlay2
            else layer = *(int *)(char *)ebbInData;
            // Get overlay
            ec = GetOverlayOut(layer, ebbOutData);

            break;

        case 10: {
            // Disable overlay
            if ((char *)ebbInData == NULL) layer = 2; // default: overlay2
            else layer = *(int *)(char *)ebbInData;
            Display_DisableOverlay(&m_disCxt, layer);
            Address userFrameBufAddr = m_userFrameBufAddr[layer];
            if (userFrameBufAddr) {
                DzIoUnmap(userFrameBufAddr, m_userFrameBufSize[layer]);
                m_userFrameBufAddr[layer] = NULL;
            }
            break;
        }

        case 11:
            // Turn on the backlight.
            BackLight_Open(&m_disCxt);

            break;

        case 12:
            // Turn off the backlight.
            BackLight_Close(&m_disCxt);
            break;

        case 13:
            // Set brightness 0~10
            if ((char *)ebbInData == NULL) layer = 10; // default brightness, full ON
            else layer = *(int *)(char *)ebbInData;

            BackLight_SetBrightness(&m_disCxt, layer);

            break;

        case 14:
        {
            BYTE transLvl;

            if ((char *)ebbInData == NULL) transLvl = 255; // default
            else transLvl = *(BYTE *)(char *)ebbInData;

            LcdCtrl_SetTransLevel(&m_disCxt, transLvl);

            break;
        }

        case 1000:// 0x0 Suspend Graceful
                  // 0x1 Suspend Immediate
        {
            int type;

            // Keep LCD light on when using overlay2.
            // Overlay1 isn't supported now.
            if (m_disCxt.overlay[2].Enable) break;

            if ((char *)ebbInData == NULL) {
                type = 0x0;//default 0x0 Suspend Graceful
            }
            else {
                type = *(int *)(char *)ebbInData;
            }
            Display_Suspend(&m_disCxt, type, 0);
            BackLight_Close(&m_disCxt);
        }
            break;

        case 1001:
            Display_Resume(&m_disCxt, 0);
            BackLight_Open(&m_disCxt);
            break;

        case 1002:
            Display_Resume(&m_disCxt, 1);
            BackLight_Open(&m_disCxt);
            break;

        case 1003:
            // Turn off the Keypad Led.
            break;

        case 1004:
            // Turn on the Keypad LED.
            break;

        default:
            ec = E_NOT_IMPLEMENTED;
            break;

    }

    return ec;

}

void CDisplay::Dispose()
{

}

DisplayContext *g_pdisCxt;

EXTERN IDeviceDriver * CDECL CreateDisplay(uint_t uDeviceNo, void *pvParameter)
{
    ECode ec;

    // Create display object
    ec = Display_Create(&s_display.m_disCxt);
    if (FAILED(ec)) {
        kprintf("Create display error!\n");
        return NULL;
    }

    g_pdisCxt = &s_display.m_disCxt;

    kprintf("Initialize display OK!\n");

    //cls

    return &s_display;
}

EXTERN_C OverlayFormat *GetDisplayOverlayFormat(int layer)
{
    if(layer != 1 && layer != 2)return NULL;
    return &g_pdisCxt->overlay[layer];
}

// For camera
EXTERN_C BOOL Display_ChangeOverlay2Format(BOOL bRecover, OverlayFormat *pTempFormat)
{
    static OverlayFormat s_backOverlay2;
    OverlayFormat *pNewOverlay2;

    if (!g_pdisCxt->overlay[2].Enable) return FALSE;

    if (bRecover) {
        pNewOverlay2 = &s_backOverlay2;
    }
    else {
        pNewOverlay2 = pTempFormat;
        memcpy(&s_backOverlay2, &g_pdisCxt->overlay[2], sizeof(OverlayFormat));
    }

    Display_DisableOverlay(g_pdisCxt, 2);

    if (NOERROR == Display_SetOverlay(g_pdisCxt, pNewOverlay2)) {
        // Clear overlay2 framebuffer
        // Because the buffer size is large enough,
        // the buffer isn't be overwritten even if
        // actual overlay buffer size is smaller
        memset((void *)g_pdisCxt->vaOverlay2Y, 0, OVERLAY2Y_FB_MAXSIZE);
        memset((void *)g_pdisCxt->vaOverlay2Cb, 0x80, OVERLAY2Cb_FB_MAXSIZE);
        memset((void *)g_pdisCxt->vaOverlay2Cr, 0x80, OVERLAY2Cr_FB_MAXSIZE);
        return TRUE;
    }
    else return FALSE;
}
